package com.ztesoft.book.common.utils.context;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;

import java.lang.reflect.Field;


/**
 * @Description 从代理类中获取真实类工具
 * @Author YangFan
 * @Date 2021-07-14 11:18
 */
@Slf4j
public final class AopTargetUtils {

    private AopTargetUtils() {
    }

    /**
     * 方法功能描述:
     * < 获取代理类的真实类 >
     *
     * @param proxy 【参数 1 注释说明】
     * @return Object                        【返回值 注释说明】
     * @author YangFan
     * @date 2021-07-14 11:15
     */
    public static Object getTarget(Object proxy) {
        if (!AopUtils.isAopProxy(proxy)) {
            return proxy;
        }
        //判断是jdk还是cglib代理
        if (AopUtils.isJdkDynamicProxy(proxy)) {
            proxy = getJdkDynamicProxyTargetObject(proxy);
        }
        else {
            proxy = getCglibProxyTargetObject(proxy);
        }
        return getTarget(proxy);
    }


    /**
     * 方法功能描述:
     * < 获取Cglib动态代理的真实类 >
     *
     * @param proxy 【参数 1 注释说明】
     * @return Object                        【返回值 注释说明】
     * @author YangFan
     * @date 2021-07-14 11:17
     */
    private static Object getCglibProxyTargetObject(Object proxy) {
        Object target = null;
        try {
            Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
            h.setAccessible(true);
            Object dynamicAdvisedInterceptor = h.get(proxy);
            Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
            advised.setAccessible(true);
            target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
        }
        catch (Exception e) {
            log.error("getCglibProxyTargetObject", e);
        }
        return target;
    }


    /**
     * 方法功能描述:
     * < 获取JDK动态代理的真实类 >
     *
     * @param proxy 【参数 1 注释说明】
     * @return Object                        【返回值 注释说明】
     * @author YangFan
     * @date 2021-07-14 11:16
     */
    private static Object getJdkDynamicProxyTargetObject(Object proxy) {
        Object target = null;
        try {
            Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
            h.setAccessible(true);
            AopProxy aopProxy = (AopProxy) h.get(proxy);
            Field advised = aopProxy.getClass().getDeclaredField("advised");
            advised.setAccessible(true);
            target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
        }
        catch (Exception e) {
            log.error("getJdkDynamicProxyTargetObject", e);
        }
        return target;
    }


}
